package com.zws.cucumber.expander;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zws.cucumber.glue.ZwsVariableStepDef;
import com.zws.cucumber.util.colorfullog.ZwsColorfulLogger;
import com.zws.cucumber.var.ZwsVar;
import org.slf4j.Logger;

import java.math.BigDecimal;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

/**
 * author: zws
 */
public class ZwsStr {

    static {
        ZwsStrBuiltInFunctions.init();
    }

    private static final Logger logger = ZwsColorfulLogger.of(ZwsStr.class);

    public static String expand(String input) {
        return expand(input, null);
    }

    public static String expand(boolean debug, String input) {
        return expand(debug, input, null);
    }

    public static String expand(String input, Map<String, Object> localVariables) {
        return expand(false, input, localVariables);
    }

    public static String expand(boolean debug, String input, Map<String, Object> localVariables) {
        return ZwsStrExpander.expand(input, null, debug, 0, localVariables);
    }

    public static String expandTemplate(String template, Map<String, Object> argumentMap, Collection<String> pendingResolutions) {
        return ZwsStrTemplateExpander.expandTemplate(template, null, false, 0, argumentMap, pendingResolutions);
    }

    public static void main(String[] args) throws JsonProcessingException {
        // expand with default value
        assertEquals("A B C", expand("@{param ? A B C }"));
        assertEquals("A B C", expand("@{param ? 'A B C' }"));
        assertEquals("A,B,C", expand("@{param ? A,B,C }"));
        assertEquals("A,B,C", expand("@{param ? 'A,B,C' }"));
        assertEquals("A, B, C ,", expand("@{param ? A, B, C , }"));
        assertEquals("A, B, C ,", expand("@{param ? 'A, B, C ,' }"));
        assertEquals("A B C  ", expand("@{param ? A B C #{str.blank} }"));
        assertEquals("A B C  ", expand("@{param ? 'A B C  ' }"));
        assertEquals("A B C     ", expand("@{param ? A B C #{str.blank: 4 } }"));
        assertEquals("A B C     ", expand("@{param ? 'A B C     ' }"));
        assertEquals("  A B C", expand("@{param ? #{str.blank} A B C }"));
        assertEquals("  A B C", expand("@{param ? '  A B C' }"));
        assertEquals("     A B C", expand("@{param ? '     A B C' }"));
        assertEquals(" ", expand("@{param ?   #{str.blank}  }"));
        assertEquals(" ", expand("@{param ?   ' '  }"));
        assertEquals("   ", expand("@{param ?   #{str.blank} #{str.blank}  }"));
        assertEquals("   ", expand("@{param ?   '   '  }"));
        assertEquals("     ", expand("@{param ?   #{str.blank: 2} #{str.blank:2 }  }"));
        assertEquals("{ }", expand("@{param ?   #{bl} #{br}  }"));
        assertEquals("{ }", expand("@{param ?   '{ }'  }"));
        assertEquals("A },{ B", expand("@{param ?   A #{br},#{bl} B  }"));
        assertEquals("A },{ B", expand("@{param ?   'A },{ B'  }"));
        assertEquals("A },'{ B", expand("@{param ?   'A },''{ B'  }"));
        assertEquals("A },''{ B'", expand("@{param ?   'A },''''{ B'''  }"));
        assertEquals("", expand("@{param ? }"));
        assertEquals("", expand("@{param ? '' }"));
        // expand with arguments
        ZwsVar.put("template", "'@{0}' and '@{1}'");
        assertEquals("'A B C' and 'D E F'", expand("@{template: A B C, D E F}"));
        assertEquals("'A B C' and 'D E F'", expand("@{template: 'A B C', 'D E F'}"));
        assertEquals("'A,B,C' and 'D,E,F'", expand("@{template: A#{comma}B#{comma}C, D#{comma}E#{comma}F}"));
        assertEquals("'A,B,C' and 'D,E,F'", expand("@{template: 'A,B,C', 'D,E,F'}"));
        assertEquals("'A  B__C  D' and '  '", expand("@{template: @{param ? A  B__C  D  }, #{str.blank:2 }  }"));
        assertEquals("'A  B__C  D' and '  '", expand("@{template: @{param ? A  B__C  D  }, '  '  }"));
        //
        ZwsVar.clearAllVariables();
//        ZwsVar.put("A", "@{B}");
//        ZwsVar.put("B", "@{A}");
//        expand("@{A}");
        expand("#{sha_256: abc, asdf}");
        expand("#{sha_256: abc, asdf2}");
        expand("#{str.upper: #{sha_256: abc, asdf}}");
        expand("#{str.lower: #{str.upper: #{sha_256: abc, asdf}}}");
        expand("#{str.lower: #{str.upper: #{sha_256: abc, asdf}}}");

        ZwsVar.put("a", "aa");
        ZwsVar.put("b", "b@{a}b");
        ZwsVar.put("c", "c@{b}c");
        expand("@{c}");

        expand("#{str.lower: #{str.upper: #{sha_256: abc, asdf}}}");

        ZwsVar.put("t", "@{0} - @{1}");
        expand("@{t: @{c}, @{a}}");

        expand("#{json.render_prop: key1, }");
        expand("#{json.render_prop: key1, '   '}");
        expand("#{json.render_prop: key1, ' A,\"  '}");
        expand("#{json.render_prop: key2, val2}");
        expand("#{json.render_prop: key3, null}");

        expand("#{json.render_prop_at_last: key1, }");
        expand("#{json.render_prop_at_last: key2, val2}");
        expand("#{json.render_prop_at_last: key3, null}");

        expand("#{if_then: #{is_empty: }, hello}");
        expand("#{if_then: #{is_not_empty: }, hello}");
        expand("#{if_then: #{is_empty: 123}, hello}");
        expand("#{if_then: #{is_not_empty: 123}, hello}");

        expand("#{!if_then: #{and: #{is_empty: }, #{is_not_empty: 123}}, hello}");
        expand("#{!if_then: #{and: #{is_empty: 123}, #{is_not_empty: 123}}, hello}");
        expand("#{!if_then: #{or: #{is_empty: 123}, #{is_not_empty: 123}}, hello}");

        expand("#{true}");
        expand("#{false}");

        expand("#{not: #{true}}");
        expand("#{not: #{false}}");

        expand("#{and: #{true}, #{true}}");
        expand("#{and: #{false}, #{true}}");
        expand("#{and: #{true}, #{false}}");
        expand("#{and: #{false}, #{false}}");

        expand("#{or: #{true}, #{true}}");
        expand("#{or: #{false}, #{true}}");
        expand("#{or: #{true}, #{false}}");
        expand("#{or: #{false}, #{false}}");

        ObjectMapper objectMapper = new ObjectMapper();

        Object obj = objectMapper.readValue("[1, 1.0, 2147483647, 9223372036854775807, 92233720368547758071]", List.class);
//        obj = objectMapper.readValue("[1, 2147483647, 9223372036854775807]", List.class);
        System.out.println(obj);
        BigDecimal d1 = new BigDecimal("10");
        BigDecimal d2 = new BigDecimal("10.0");
        System.out.println(d1.compareTo(d2));

        ZwsVar.clearAllVariables();
        ZwsVar.put("test_json", "{\"key1\": \"val1\"}");
        ZwsVar.put("test_json2", Map.of("key2", "val2"));
        ZwsVariableStepDef step = new ZwsVariableStepDef();
        step.printVariables();
        expand("@{test_json $.key1}");
        expand("@{!test_json2 $.key2}");

        ZwsVar.put("resp", "{\"err_code\": \"invalid_request\", \"err_desc\": \"Invalid Request\"}");
        assertEquals(
                """
                        {
                            "error": "invalid_request",
                            "error_description": "Invalid Request"
                        }
                        """,
                expand(
                        """
                        {
                            "error": "@{resp $.err_code}",
                            "error_description": "@{resp $.err_desc}"
                        }
                        """
                )
        );

        // groovy expr
        assertEquals("2", expand("`1+1`"));
        assertEquals("2", expand("`1+1`"));
        assertEquals("2", expand("`1+1`"));
        assertEquals(" 1 + 1 = 2 ", expand(" 1 + 1 = ` 1 + 1 ` "));
        assertTrue(System.currentTimeMillis() < Long.parseLong(expand("`System.currentTimeMillis() + 1000`")));

        ZwsVar.clearAllVariables();
        ZwsVar.put("expected", "ABC");
        assertEquals("true", expand("`expected.equals(\"ABC\")`"));
        assertEquals("true", expand("`\"@{!expected}\".equals(\"ABC\")`"));
        assertEquals(
                """
                { "error": "invalid_request", "error_description": "uid should not be 2" }
                """,
        ZwsStrExpander.expand(
                """
                { "error": "invalid_request", "error_description": "uid should not be `1 + 1`" }
                """,
                null, true, 0, null));
        assertEquals("`", expand(true,"`return \"``\"`"));
        assertEquals("abc`cde", expand("abc``cde"));
        assertEquals("`abccde", expand("``abccde"));
        assertEquals("abccde`", expand("abccde``"));

        ZwsVar.put("temp0", "{ k1: @{0}, k2: @{1} }");
        ZwsVar.put("temp1", "{ k1: @{0 ? \"v1\"}, k2: @{1 ? 123} }");
        ZwsVar.put("temp2", "@{temp1: @{0}, @{1}}");
        ZwsVar.put("temp3", "@{temp2: @{0}, @{1}}");
        ZwsVar.put("temp4", "@{temp1: @{0 ? \"v4\"}, @{1 ? 456} }");
        ZwsVar.put("temp5", "@{temp4: @{0}, @{1}}");
        ZwsVar.put("temp6", "@{temp0: @{0 ? \"v7\"}, @{1} }");
        ZwsVar.put("temp7", "@{temp6: @{0}, @{1}}");

        assertEquals("{ k1: \"v3\", k2: 789 }", expand("@{temp3: \"v3\", 789}"));
        assertEquals("{ k1: \"v1\", k2: 123 }", expand("@{!temp3:}"));
        assertEquals("{ k1: \"v1\", k2: 123 }", expand("@{temp3: ,}"));
        assertEquals("{ k1: , k2: 123 }", expand("@{temp3: '',}"));
        assertEquals("{ k1: \"v1\", k2:  }", expand("@{temp3: , '' }"));
        assertEquals("{ k1: , k2:  }", expand("@{temp3: '', '' }"));

        assertEquals("{ k1: \"v3\", k2: 789 }", expand("@{temp5: \"v3\", 789}"));
        assertEquals("{ k1: \"v4\", k2: 456 }", expand("@{temp5:}"));
        assertEquals("{ k1: \"v4\", k2: 456 }", expand("@{temp5: ,}"));
        assertEquals("{ k1: , k2: 456 }", expand("@{temp5: '',}"));
        assertEquals("{ k1: \"v4\", k2:  }", expand("@{temp5: , '' }"));
        assertEquals("{ k1: , k2:  }", expand("@{temp5: '', '' }"));

        assertEquals("{ k1: \"v3\", k2: 789 }", expand("@{temp7: \"v3\", 789}"));
        assertEquals("{ k1: \"v7\", k2:  }", expand("@{temp7:}"));
        assertEquals("{ k1: \"v7\", k2:  }", expand("@{temp7: ,}"));
        assertEquals("{ k1: , k2:  }", expand("@{temp7: '',}"));
        assertEquals("{ k1: \"v7\", k2:  }", expand("@{temp7: , '' }"));
        assertEquals("{ k1: , k2:  }", expand("@{temp7: '', '' }"));


        System.out.println(System.identityHashCode(""));
        System.out.println(System.identityHashCode(new String("")));
        System.out.println(System.identityHashCode(new String("")));
    }

}
